# coding: utf-8
import json

from bluesky.converter import resource_converter
from bluesky.dto.exception import BizException, not_exist
from bluesky.repository import resource_repository
from bluesky.service.inner.resource import r_handler
from bluesky.service.inner.resource.r_handler import ResourceHandlerCtx, handler_map
from bluesky.util import biz


# 检测是否支持该类型的接口调用
def check_support(rtype, action):
    config = biz.get_resource_type_config(rtype, action)
    if not config:
        raise BizException(not_exist.code, "接口不存在", None)


def search_resources(rtype, action, data, request_ctx):
    check_support(rtype, action)
    rss = search_page_by_config(rtype, action, data, request_ctx)

    ctx: ResourceHandlerCtx = page_operate(rtype, rss, action, data, None, request_ctx)
    if ctx.result and ctx.result['records']:
        list(map(lambda x: biz.remove_fields(x, action), ctx.result['records']))
    return ctx


def add_is_delete_param(query_json, config):
    if not query_json:
        return
    # 如果想查询is_delete=1的数据，可以在query里加上 is_delete 的key
    if 'is_delete' not in query_json:
        query_json['is_delete'] = 0


# 根据config查询数据
def search_page_by_config(rtype, action, data, request_ctx):
    config = biz.get_resource_type_config(rtype, action)
    if not config:
        return None
    sql_tpl = config.get('sql')
    query = config.get('query')
    auto_page = config.get('auto_page')
    session = request_ctx.get('session')
    if query:
        # 根据配置的query 用参数替换成真正的查询参数
        tpl_str = json.dumps(query)
        query_str = biz.render(tpl_str, request_ctx)
        query_json = json.loads(query_str)
        add_is_delete_param(query_json, config)
        # 根据query查询
        if auto_page is None or auto_page:
            page_num, page_size = biz.get_page_num(data), biz.get_page_size(data)
            total = resource_repository.select_count(rtype, query_json)
            records = []
            if total > 0:
                records = resource_repository.select_list(session, rtype, query_json)
            return {'records': records, 'page_num': page_num, 'page_size': page_size, 'total': total}
        else:
            records = resource_repository.select_list(session, rtype, query_json)
            return {'records': records}
    elif sql_tpl:
        # 根据sql查询
        sql = biz.render(sql_tpl, request_ctx)
        if auto_page is None or auto_page:
            page_num, page_size = biz.get_page_num(data), biz.get_page_size(data)
            data_sql = sql + f' limit {(page_num - 1) * page_size}, {page_size}'

            count_sql = f'select count(*) as cnt from ({sql}) _tmp_table'
            total = resource_repository.count_by_sql(rtype, count_sql)
            data = []
            if total > 0:
                data = resource_repository.list_by_sql(session, rtype, data_sql)
            return {'records': data, 'page_num': page_num, 'page_size': page_size, 'total': total}
        else:
            data = resource_repository.list_by_sql(session, rtype, sql)
            return {'records': data}


def search_one(rtype, action, data, request_ctx):
    check_support(rtype, action)
    resource = search_one_by_config(rtype, action, request_ctx)
    ctx: ResourceHandlerCtx = operate(rtype, resource, action, data, None, request_ctx)
    biz.remove_fields(ctx.result, action)
    return ctx


# 根据config查询数据
def search_one_by_config(rtype, action, request_ctx):
    check_support(rtype, action)
    config = biz.get_resource_type_config(rtype, action)
    if not config:
        return None
    sql_tpl = config.get('sql')
    query = config.get('query')
    session = request_ctx.get('session')
    if query:
        # 根据配置的query 用参数替换成真正的查询参数
        tpl_str = json.dumps(query)
        query_str = biz.render(tpl_str, request_ctx)
        query_json = json.loads(query_str)
        query_json['page_num'] = 1
        query_json['page_size'] = 1
        add_is_delete_param(query_json, config)
        records = resource_repository.select_list(session, rtype, query_json)
        return None if len(records) <= 0 else records[0]
    elif sql_tpl:
        # 根据sql查询
        sql = biz.render(sql_tpl, request_ctx)
        return resource_repository.one_by_sql(session, rtype, sql)


def detail(rtype, rid, request_ctx):
    session = request_ctx.get('session')
    resource = resource_repository.one(session, rtype, rid)
    action = 'get'
    check_support(rtype, action)
    ctx: ResourceHandlerCtx = operate(rtype, resource, action, None, None, request_ctx)
    biz.remove_fields(ctx.result, action)
    return ctx


def create_resource(rtype, param, request_ctx):
    action = 'create'
    check_support(rtype, action)
    resource = resource_converter.init_from_dto_create(rtype, action, param)
    ctx: ResourceHandlerCtx = operate(rtype, resource, action, param, insert, request_ctx)
    biz.remove_fields(ctx.result, action)
    return ctx


def opr_resource(rtype, rid, action, param, request_ctx):
    check_support(rtype, action)
    session = request_ctx.get('session')
    resource = resource_repository.one(session, rtype, rid)
    ctx: ResourceHandlerCtx = operate(rtype, resource, action, param, update, request_ctx)
    biz.remove_fields(ctx.result, action)
    return ctx


def delete_resource(rtype, rid, action, param, request_ctx):
    check_support(rtype, action)
    session = request_ctx.get('session')
    resource = resource_repository.one(session, rtype, rid)
    ctx: ResourceHandlerCtx = operate(rtype, resource, action, param, delete, request_ctx)
    biz.remove_fields(ctx.result, action)
    return ctx


def insert(ctx: ResourceHandlerCtx):
    resource = ctx.resource
    session = ctx.session
    # 保存数据
    if resource:
        # session.add([resource])
        session.add(resource)


def update(ctx: ResourceHandlerCtx):
    resource = ctx.resource
    # 保存数据
    if resource:
        resource_repository.update_with_session(resource, ctx.session)


def delete(ctx: ResourceHandlerCtx):
    resource = ctx.resource
    resource.is_delete = 1
    # 保存数据
    if resource:
        resource_repository.update_with_session(resource, ctx.session)


def operate(rtype, resource, action, param, func, request_ctx):
    ctx: ResourceHandlerCtx = r_handler.init_ctx(action, param, resource, request_ctx)

    key = f'{rtype}_{action}'
    handler = handler_map.get(key)
    if handler is not None:
        handler.before_tx(ctx)

    # 开启事务
    # session = db.session_begin()
    session = ctx.session
    try:
        # 事务前
        if handler is not None:
            resource = handler.before_handle(ctx)
            ctx.resource = resource

        # 数据库操作
        if func:
            func(ctx)

        # 事务后
        result = resource_converter.to_dto(resource)
        ctx.result = result
        if handler is not None:
            result = handler.after_handle(ctx)
            ctx.result = result

        session.commit()
    except Exception as e:
        print(e)
        if session:
            session.rollback()
        raise e
    finally:
        if session:
            session.close()

    if handler is not None:
        result = handler.after_tx(ctx)
        ctx.result = result
    return ctx


def page_operate(rtype, resource, action, param, func, request_ctx):
    ctx: ResourceHandlerCtx = r_handler.init_ctx(action, param, resource, request_ctx)

    key = f'{rtype}_{action}'
    handler = handler_map.get(key)
    if handler is not None:
        handler.before_tx(ctx)

    # 开启事务
    session = ctx.session
    try:
        # 事务前
        if handler is not None:
            resource = handler.before_handle(ctx)
            ctx.resource = resource

        # 数据库操作
        if func:
            func(ctx)

        # 事务后
        result = resource_converter.to_page(resource)
        ctx.result = result
        if handler is not None:
            result = handler.after_handle(ctx)
            ctx.result = result

        session.commit()
    except Exception as e:
        print(e)
        if session:
            session.rollback()
        raise e
    finally:
        if session:
            session.close()

    if handler is not None:
        result = handler.after_tx(ctx)
        ctx.result = result
    return ctx
